/*
 * Yi Jin  A53009661
 */

#include "prefetcher.h"
#include <stdio.h>
#include <algorithm>
using namespace std;

Prefetcher::Prefetcher() {
	streamFilter = new StreamFilter();
}
Prefetcher::~Prefetcher() {
	delete streamFilter;
}
bool Prefetcher::hasRequest(u_int32_t cycle) {
	streamFilter->clean();
	if (streamFilter->epoch > 0) {
		streamFilter->epoch--;
	} else {
		streamFilter->beginEpoch();
	}
	return !reqs.empty();
}

Request Prefetcher::getRequest(u_int32_t cycle) {
	return _nextReq;
}

void Prefetcher::completeRequest(u_int32_t cycle) {
	reqs.pop();
	if (!reqs.empty()) {
		_nextReq.addr = reqs.front();
	}
}

void Prefetcher::cpuRequest(Request req) {
	int length = streamFilter->offer(req);
	if (!req.HitL1 && reqs.size() < QUEUE_SIZE/4) {
		int stride = 32;
		if (length < 0) {
			stride *= -1;
			length *= -1;
		}
		bool isEmpty = reqs.empty();
		if(length == 0)
		{
			for (int i = 0; i < FETCH/2; i++) {
				if (reqs.size() < QUEUE_SIZE) {
					reqs.push(req.addr + stride * (i + 1));
				}
			}
		}
		else
		{
			for (int i = 0; i < length * FETCH; i++) {
				if (reqs.size() < QUEUE_SIZE) {
					reqs.push(req.addr + stride * (i + 1));
				}
			}
		}
		if (isEmpty && !reqs.empty()) {
			_nextReq.addr = reqs.front();
		}
	}
}
StreamFilter::StreamFilter() {
	size = 0;
	epoch = EPOCH;
}
int StreamFilter::offer(Request req) {
	int currLength = 0;
	bool direction = true;
	for (int i = 0; i < FILTER_SIZE; i++) {
		if (streamFilterSlots[i].life > 0) {
			if (req.addr <= streamFilterSlots[i].addr + 16
					&& req.addr >= streamFilterSlots[i].addr) {
				if (req.load) {
					streamFilterSlots[i].addr = req.addr;
					streamFilterSlots[i].length++;
					streamFilterSlots[i].direction = true;
					streamFilterSlots[i].life++;
					currLength = streamFilterSlots[i].length;
					direction = streamFilterSlots[i].direction;
				} else {
					invalidate(i);
				}
			}
			if (req.addr >= streamFilterSlots[i].addr - 16
					&& req.addr < streamFilterSlots[i].addr) {
				if (req.load) {
					streamFilterSlots[i].addr = req.addr;
					streamFilterSlots[i].length++;
					streamFilterSlots[i].life++;
					streamFilterSlots[i].direction = false;
					currLength = streamFilterSlots[i].length;
					direction = streamFilterSlots[i].direction;
				} else {
					invalidate(i);
				}
			}
		}
	}
	if (currLength == 0) {
		if (req.load) {
			if (size < FILTER_SIZE) {
				for (int i = 0; i < FILTER_SIZE; i++) {
					if (streamFilterSlots[i].life == 0) {
						streamFilterSlots[i].life = LIFETIME;
						streamFilterSlots[i].addr = req.addr;
						streamFilterSlots[i].direction = true;
						streamFilterSlots[i].length = 0;
						size++;
						break;
					}
				}
			}
			else
			{
				LHTnext[0]++;
			}
		}
	}
	int i = 0;
	while (currLength + 1 + i < MAX_STREAM) {
		if (LHTcurr[currLength] >= 2 * LHTcurr[currLength + 1 + i]) {
			break;
		}
		i++;
	}
	if (direction) {
		return i;
	} else {
		return - i;
	}
}
void StreamFilter::invalidate(int slot) {
	streamFilterSlots[slot].life = 0;
	int length = streamFilterSlots[slot].length;
	for (int i = 0; i <= length; i++) {

		LHTnext[i] += length + 1;
		if (LHTnext[i] > MAX_LENGTH) {
			LHTnext[i] = MAX_LENGTH;
		}
		LHTcurr[i] -= length + 1;
		if (LHTcurr[i] < 0) {
			LHTcurr[i] = 0;
		}
	}
	size--;
}
void StreamFilter::beginEpoch() {
	for (int i = 0; i < FILTER_SIZE; i++) {
		if (streamFilterSlots[i].life > 0) {
			invalidate(i);
		}
	}
	epoch = EPOCH;
	for (int i = 0; i < MAX_STREAM; i++) {
		LHTcurr[i] = LHTnext[i];
		LHTnext[i] = 0;
	}
	int i = LHTcurr[0];
}
void StreamFilter::clean() {
	for (int i = 0; i < FILTER_SIZE; i++) {
		if (streamFilterSlots[i].life > 0) {
			streamFilterSlots[i].life--;
			if (streamFilterSlots[i].life == 0) {
				invalidate(i);
			}
		}
	}
}

